/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.framework.concurrent.locks;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.StampedLock;
import java.util.logging.Logger;

/**
 * {@link java.util.concurrent.locks.StampedLock} Demo.
 *
 * <p>StampedLock 是为了优化可重入读写锁（ReentrantReadWriteLock）性能的一个锁实现工具，<b>不支持重入</b>。</p>
 *
 * @author liym
 * @since 2020-05-10 20:02 新建
 */
final class StampedLockDemo {

    private static final Logger LOGGER = Logger.getLogger(StampedLockDemo.class.getName());

    private static final StampedLock STAMPED_LOCK = new StampedLock();

    /** 模拟缓存中存储的数据 */
    private static final Map<String, String> MAP_CACHE = new HashMap<>(8);
    /** 模拟数据库存储的数据 */
    private static final Map<String, String> MAP_DB = new HashMap<>(8);

    private StampedLockDemo() {}

    static {
        MAP_DB.put("zhangsan", "你好，我是张三");
        MAP_DB.put("sili", "你好，我是李四");
    }

    private static String getInfo(String name) {
        // 获取悲观读
        long stamp = STAMPED_LOCK.readLock();
        LOGGER.info("线程名：" + Thread.currentThread().getName() + " 获取了悲观读锁" + " 用户名：" + name);
        try {
            if ("zhangsan".equals(name)) {
                LOGGER.info("线程名：" + Thread.currentThread().getName() + " 休眠中" + " 用户名：" + name);
                Thread.sleep(3000);
                LOGGER.info("线程名：" + Thread.currentThread().getName() + " 休眠结束" + " 用户名：" + name);
            }
            String info = MAP_CACHE.get(name);
            if (null != info) {
                LOGGER.info("在缓存中获取到了数据");
                return info;
            }
        } catch (InterruptedException e) {
            LOGGER.info("线程名：" + Thread.currentThread().getName() + " 释放了悲观读锁");
            e.printStackTrace();
        } finally {
            // 释放悲观读
            STAMPED_LOCK.unlock(stamp);
        }
        // 获取写锁
        stamp = STAMPED_LOCK.writeLock();
        LOGGER.info("线程名：" + Thread.currentThread().getName() + " 获取了写锁" + " 用户名：" + name);
        try {
            // 判断一下缓存中是否被插入了数据
            String info = MAP_CACHE.get(name);
            if (null != info) {
                LOGGER.info("获取到了写锁，再次确认在缓存中获取到了数据");
                return info;
            }
            // 这里是往数据库获取数据
            String infoByDb = MAP_DB.get(name);
            if (infoByDb != null) {
                // 将数据插入缓存
                MAP_CACHE.put(name, infoByDb);
                LOGGER.info("缓存中没有数据，在数据库获取到了数据");
                return infoByDb;
            }
        } finally {
            // 释放写锁
            LOGGER.info("线程名：" + Thread.currentThread().getName() + " 释放了写锁" + " 用户名：" + name);
            STAMPED_LOCK.unlock(stamp);
        }
        return null;
    }

    public static void main(String[] args) {
        // 线程1
        Thread t1 = new Thread(() -> {
            String info = getInfo("zhangsan");
            System.out.println("t1 getInfo: " + info);
        });
        // 线程2
        Thread t2 = new Thread(() -> {
            String info = getInfo("lisi");
            System.out.println("t2 getInfo: " + info);
        });
        // 线程启动
        t1.start();
        t2.start();
        // 线程同步
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
